﻿using System;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using Common;

namespace ReviewerServiceLibrary
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
    public class ReviewerService: IReviewerService
    {
        private DataContext context = null;
        private const int ServiceID = 3;

        public ReviewerService()
        {
            context = new DataContext();
            Logger.type = "reviewer";
        }

        public Response CreateReview(string articleID, string attachmentID, string authorID, int decision)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                var article = (from a in context.Articles where a.ID == articleID select a).SingleOrDefault();
                if (article == null)
                {
                    response.Error = new Error()
                    {
                        Details = "Article with ID '" + articleID + "' not found.",
                        TargetSite = MethodBase.GetCurrentMethod().Name,
                        Type = ErrorTypes.ObjectNotFound
                    };
                }
                else
                {
                    var reviewed = (from r in context.ReviewedArticles where r.ArticleID == articleID select r).SingleOrDefault();
                    if (reviewed == null)
                    {
                        response.Error = new Error()
                        {
                            Details = "Reviewer with id '" + authorID + "' did not take article for review. Looks like the request is malformed",
                            TargetSite = MethodBase.GetCurrentMethod().Name,
                            Type = ErrorTypes.ObjectNotFound
                        };
                    }
                    else
                    {
                        var review = new Review()
                        {
                            ArticleID = articleID,
                            AttachmentID = attachmentID,
                            AuthorID = authorID,
                            Decision = (ReviewDecision)decision,
                            Status = ReviewStatus.Created
                        };
                        lock (context)
                        {
                            article.Stage = MaturityStages.ReviewCreated;
                            article.ArticleReviewer = authorID;
                            context.ReviewedArticles.Remove(reviewed);
                            context.Reviews.Add(review);
                            context.SaveChanges();
                        }
                        response.Result.Add(review);
                    }
                }
            }
            catch (Exception ex)
            {
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response GetFilteredArticles()
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                int sentForReview = (int)MaturityStages.SentForReview;

                var articles = (from a in context.Articles where a.status == sentForReview select a).ToList();

                if (articles.Count == 0)
                {
                    response.Error.Details = "Articles for review not found";
                    response.Error.TargetSite = MethodBase.GetCurrentMethod().Name;
                    response.Error.Type = ErrorTypes.ObjectNotFound;
                }
                else
                {
                    response.Result.AddRange(articles);
                }
            }
            catch (Exception ex)
            {
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response GetReviews(string authorID)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                var reviews = (from r in context.Reviews where r.AuthorID == authorID select r).ToList();

                if (reviews.Count == 0)
                {
                    response.Error.Details = "No reviews found for this author id: " + authorID;
                    response.Error.TargetSite = MethodBase.GetCurrentMethod().Name;
                    response.Error.Type = ErrorTypes.ObjectNotFound;
                }
                else
                {
                    foreach (var r in reviews)
                    {
                        response.Result.Add(r);
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response GetReviewDetails(string reviewID)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                var review = (from r in context.Reviews where r.ID == reviewID select r).SingleOrDefault();

                if (review == null)
                {
                    response.Error.Details = "No reviews found with id: " + reviewID;
                    response.Error.TargetSite = MethodBase.GetCurrentMethod().Name;
                    response.Error.Type = ErrorTypes.ObjectNotFound;
                }
                else
                {
                    response.Result.Add(review);
                }
            }
            catch (Exception ex)
            {
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response TakeArticleForReview(string articleID, string reviewerID)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                var article = (from a in context.Articles where a.ID == articleID select a).SingleOrDefault();
                
                if (article == null)
                {
                    response.Error.Details = "Article with id: " + articleID + "' not found";
                    response.Error.TargetSite = MethodBase.GetCurrentMethod().Name;
                    response.Error.Type = ErrorTypes.ObjectNotFound;
                }
                else
                {
                    var exists = (from ra in context.ReviewedArticles where ra.ArticleID == articleID select ra).SingleOrDefault();

                    if (exists != null)
                    {
                        response.Error.Details = "Sorry, article with id '" + articleID + "' has alredy been taken for review.";
                        response.Error.TargetSite = MethodBase.GetCurrentMethod().Name;
                        response.Error.Type = ErrorTypes.ObjectExists;
                    }
                    else
                    {
                        var articleForReview = new ReviewedArticle()
                        {
                            ArticleID = articleID,
                            ReviewerID = reviewerID
                        };

                        lock (context)
                        {
                            article.Stage = MaturityStages.OnReview;
                            context.ReviewedArticles.Add(articleForReview);
                            context.SaveChanges();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response GetArticlesForReview(string reviewerID)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                var articles = (from ea in context.ReviewedArticles from a in context.Articles where ea.ReviewerID == reviewerID && ea.ArticleID == a.ID select a).ToList();
                if (articles.Count == 0)
                {
                    response.Error.Details = "No articles for review found for reviever: '" + reviewerID + "'";
                    response.Error.TargetSite = MethodBase.GetCurrentMethod().Name;
                    response.Error.Type = ErrorTypes.ObjectNotFound;
                }
                else
                {
                    response.Result.AddRange(articles);
                }
            }
            catch (Exception ex)
            {
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }
    }
}